﻿using UnityEngine;
using System.Collections;
using System.Collections.Generic;


public class Dungenon
{
	public static int noteSize = 3;
	public int map_height;
	public int map_width;
	public MapNote[,] map;
	public MapNote startNote;
	public MapNote endNote;
	public List<Room> roomList;
	public List<MapNote> doorNoteList;
	public List<MapNote> wayNoteList;
	private Queue findQueue;
	public Dungenon(int width, int height, int roomtimes, int seed = 0)
	{
		if(seed != 0)
		{
			Random.InitState(100);
		}
		map_width = width;
		map_height = height;
		map = new MapNote[map_width,map_height];
		roomList = new List<Room>();
		doorNoteList = new List<MapNote>();
		wayNoteList = new List<MapNote>();
		findQueue = new Queue();

		//初始化地图
		IniMap();
		Room room;
		AddRoom(RandRoom(RoomType.stair_up));
		do{
			room = RandRoom(RoomType.stair_down);
		}while (!AddRoom(room));
		while (roomtimes > 0) 
		{
			AddRoom(RandRoom());
			roomtimes--;
		}
		FindWay(false);
		FindUnconnectedDoor();
		ClearMapMark();
		FindWay(true);
		ClearExcessWay();
		ClearDeadWay();
	}

#region GetMapNote
	private MapNote GetMapNote(int x, int y)
	{
		return map[x,y];
	}
	private MapNote GetMapNote(float x, float y)
	{
		//return map[Mathf.RoundToInt(x/noteSize),Mathf.RoundToInt(y/noteSize)];
		return map[(int)x,(int)y];
	}
	private MapNote GetMapNote(Vector2 pos)
	{
		return GetMapNote(pos.x,pos.y);
	}
	private MapNote GetMapNote(Door door)
	{
		return GetMapNote(door.x,door.y);
	}
#endregion //GetMapNote

	private void IniMap()
	{
		for (int i = map_width-1; i >= 0; i--) 
		{
			for (int k = map_height-1; k >= 0; k--) 
			{
				map[i,k] = new MapNote(i, k, MapNoteType.nothing);
			}
		}
	}
	private Room RandRoom(RoomType roomtype = RoomType.def)
	{
		int width = Mathf.RoundToInt(Random.Range(5,10));
		int height = Mathf.RoundToInt(Random.Range(5,10));
		if (roomtype == RoomType.stair_up || roomtype == RoomType.stair_down) 
		{
			width = Mathf.RoundToInt(1);
			height = Mathf.RoundToInt(1);
		}
		Room room = new Room(width, height, roomtype);
		room.SetPos( new Vector2(Random.Range(1,map_width - width - 1),Random.Range(1,map_height - height - 1)) );
		return room;
	}
	private bool CheckRoom(Room check)
	{
		if (
				check.currentStart.x < 1 ||
				check.currentStart.y < 1 || 
				check.currentEnd.x > map_width - 1 ||
				check.currentEnd.y > map_height - 1
			)
		{
			return false;
		}
		Room room;
		for (int i = roomList.Count-1; i >= 0; i--) 
		{
			room = roomList[i];
			if (!(
					check.currentStart.x - room.currentEnd.x >= 1 ||
					room.currentStart.x - check.currentEnd.x >= 1 || 
					check.currentStart.y - room.currentEnd.y >= 1 || 
					room.currentStart.y - check.currentEnd.y >= 1
				)) 
			{
				return false;
			}
		}
		return true;
	}
	private bool AddRoom(Room room)
	{
		if (!CheckRoom(room)) 
		{
			return false;
		}
		MapNote currentNote;
		roomList.Add(room);
		//设置房间的地图节点类型，并将开始节点加入路径查找
		for (float x = room.currentStart.x; x < room.currentEnd.x; x ++) 
		{
			for (float y = room.currentStart.y; y < room.currentEnd.y; y ++) 
			{
				currentNote = GetMapNote(x,y);
				switch (room.type) 
				{
					case RoomType.def:
					  currentNote.type = MapNoteType.room;
					  break;
					case RoomType.stair_up:
					  currentNote.type = MapNoteType.stair_up;
					  startNote = currentNote;
					  findQueue.Enqueue(currentNote);
					  break;
					case RoomType.stair_down:
					  currentNote.type = MapNoteType.stair_down;
					  endNote = currentNote;
					  break;
				}
			}
		}
		//设置门的地图节点类型，并将开始节点设为已连通
		for (int i = room.doors.Count-1; i >= 0; i--) 
		{
			currentNote = GetMapNote(room.doors[i]);
			doorNoteList.Add(currentNote);
			currentNote.holderDoor = room.doors[i];
			if (currentNote.type != MapNoteType.stair_up && currentNote.type != MapNoteType.stair_down) 
			{
				currentNote.type = MapNoteType.door;
			}
			else if (currentNote.type == MapNoteType.stair_up) 
			{
				currentNote.holderDoor.Connected = true;
			}
		}
		
		return true;
	}
	private void FindWay(bool IgnoreConnected)
	{
		int x;
		int y;
		MapNote currentNote;
		while (findQueue.Count > 0) 
		{
			MapNote midNote = (MapNote)findQueue.Dequeue();
			if (midNote.holderDoor.linker != null) 
			{
				continue;
			}
			for (int dx = 1; dx >= -1; dx--) 
			{
				for (int dy = 1; dy >= -1; dy--) 
				{
					if (Mathf.Abs(dx) == Mathf.Abs(dy)) 
					{
						continue;
					}
					x = midNote.x + dx;
					y = midNote.y + dy;
					if ((x >= 0 && x < map_width) && (y >= 0 && y < map_height)) 
					{
						currentNote = map[x,y];
						switch (currentNote.type) 
						{
							case MapNoteType.room:
							  continue;
							case MapNoteType.way://无视路的存在同意处理，路不会阻挡之后的路径连接
							case MapNoteType.nothing:
								if (IgnoreConnected) 
								{
									if (currentNote.holderDoor == null) 
									{
										currentNote.holderDoor = midNote.holderDoor;
										currentNote.parent = midNote;
										findQueue.Enqueue(currentNote);
									}
									else if(currentNote.holderDoor.holderRoom != midNote.holderDoor.holderRoom)
									{
										//Debug.Log("take a hide way");
										CreateWay(currentNote,true);
										currentNote.holderDoor.linker = GetMapNote(midNote.holderDoor);
										CreateWay(midNote,true);
										midNote.holderDoor.linker = GetMapNote(currentNote.holderDoor);
									}
								}
								else
								{
									if (currentNote.holderDoor != null && currentNote.holderDoor.linker == null) 
									{
										continue;
									}
									currentNote.holderDoor = midNote.holderDoor;
									currentNote.parent = midNote;
									findQueue.Enqueue(currentNote);
								}
							  break;
							case MapNoteType.stair_down:
								if (!currentNote.holderDoor.Connected && currentNote.type != MapNoteType.stair_up)
								{
									currentNote.holderDoor.Connected = true;
									CreateWay(midNote);
									midNote.holderDoor.linker = currentNote;
								}
							  break;
							case MapNoteType.door:
								if (currentNote.holderDoor != midNote.holderDoor) 
								{
									if (!currentNote.holderDoor.Connected) 
									{
										CreateWay(midNote);
										midNote.holderDoor.linker = currentNote;
										midNote.holderDoor.type = DoorType.opend;
										currentNote.holderDoor.linker = midNote;
									  	for (int i = currentNote.holderDoor.holderRoom.doors.Count-1; i >= 0; i--) 
									  	{
									  		currentNote.holderDoor.holderRoom.doors[i].Connected = true;
									  		if(currentNote.holderDoor != currentNote.holderDoor.holderRoom.doors[i])
									  		{
									  			findQueue.Enqueue(GetMapNote(currentNote.holderDoor.holderRoom.doors[i]));
									  		}
									  	}
									}
									else if (IgnoreConnected && currentNote.holderDoor.holderRoom != midNote.holderDoor.holderRoom)
									{
										//Debug.Log("take half hide way");
										CreateWay(midNote ,true);
										midNote.holderDoor.linker = currentNote;
										midNote.holderDoor.type = DoorType.hide;
									}
								}
							  break;
							default:
							  
							  break;
						}
					}
				}
			}
		}
	}
	private void ClearMapMark()
	{
		for (int i = map_width-1; i >= 0; i--) 
		{
			for (int k = map_height-1; k >= 0; k--) 
			{
				if (map[i,k].type == MapNoteType.nothing) 
				{
					map[i,k].holderDoor = null;
					map[i,k].parent = null;
				}
			}
		}
	}
	private void FindUnconnectedDoor()
	{
		// for (int i = doorNoteList.Count-1; i >= 0; i--) 
		// {
		// 	if (doorNoteList[i].holderDoor.linker == null) 
		// 	{
		// 		doorNoteList[i].holderDoor.type = DoorType.hide;
		// 		findQueue.Enqueue(doorNoteList[i]);
		// 	}
		// }
		List<MapNote> tempList = new List<MapNote>();
		for (int i = doorNoteList.Count-1; i >= 0; i--) 
		{
			if (doorNoteList[i].holderDoor.linker == null) 
			{
				tempList.Add(doorNoteList[i]);
			}
		}
		for (int i = doorNoteList.Count-1; i >= 0; i--) 
		{
			tempList.Remove(doorNoteList[i].holderDoor.linker);
		}
		for (int i = tempList.Count-1; i >= 0; i--) 
		{
			tempList[i].holderDoor.type = DoorType.hide;
			findQueue.Enqueue(tempList[i]);
		}
	}

	private void CreateWay(MapNote note ,bool hide = false)
	{
		MapNote currentNote = note;
		while (currentNote.parent != null) 
		{
			wayNoteList.Add(currentNote);
			if(hide)
			{
				currentNote.type = MapNoteType.hide_way;
				Debug.Log("yes");
			}
			else
			{
				currentNote.type = MapNoteType.way;
			}
			
			currentNote = currentNote.parent;
		}
	}

	private void ClearExcessWay()
	{
		int x;
		int y;
		for (int i = wayNoteList.Count-1; i >= 0; i--) 
		{
			x = wayNoteList[i].x;
			y = wayNoteList[i].y;
			for (int dx = -1; dx <= 1; dx += 2) 
			{
				for (int dy = -1; dy <= 1; dy += 2) 
				{
					if (Walkable(x+dx, y+dy)) 						//斜角可行走
					{
						if (Walkable(x+dx,y) && Walkable(x,y+dy))	//斜角相邻的两边可行走
						{
							if (
									(
										( !Walkable(x,y-dy) || ( Walkable(x,y-dy) && Walkable(x+dx,y-dy) ) ) &&
										( !Walkable(x-dx,y) || ( Walkable(x-dx,y) && Walkable(x-dx,y+dy) ) )
									) ||
									(
										Walkable(x,y-dy) && Walkable(x-dx,y) &&
										(Walkable(x+dx,y-dy) || Walkable(x-dx,y+dy)) &&
										Walkable(x-dx,y-dy)
									)
								) //剩余两边不可走或者可连通 
							{
								wayNoteList[i].type = MapNoteType.nothing;
							}
						}
					}
				}
			}
		}
	}

	private void ClearDeadWay()
	{
		int count;
		for (int i = wayNoteList.Count-1; i >= 0; i--) 
		{
			count = 0;
			for (int dx = 1; dx >= -1; dx--) 
			{
				for (int dy = 1; dy >= -1; dy--) 
				{
					if (Mathf.Abs(dx) == Mathf.Abs(dy)) 
					{
						continue;
					}
					if (Walkable(wayNoteList[i].x+dx,wayNoteList[i].y+dy, true)) 
					{
						count++;
					}
				}
			}
			if (count < 2)
			{
				map[wayNoteList[i].x,wayNoteList[i].y].type = MapNoteType.nothing;
			}
		}
	}

	private bool Walkable(int x, int y, bool IncludeINOUT = false)
	{
		if ((x >= 0 && x < map_width) && (y >= 0 && y < map_height))
		{
			//if (map[x,y].type != MapNoteType.nothing && map[x,y].type != MapNoteType.room)
			if (
					map[x,y].type == MapNoteType.way ||
					map[x,y].type == MapNoteType.door ||
					map[x,y].type == MapNoteType.hide_way ||
					(
						IncludeINOUT &&
						(
							map[x,y].type == MapNoteType.stair_up ||
							map[x,y].type == MapNoteType.stair_down
						)
					)
				)
			{
				return true;
			}
		}
		return false;
	}
	public void DrawMap()
	{
		Vector3 startPos = new Vector3(50f,0,50f);
		Color color = Color.red;
		Vector3 pos_1;
		Vector3 pos_2;
		Vector3 pos_3;
		Vector3 pos_4;
		for (int j = map_width-1; j >= 0; j--) 
		{
			for (int i = map_height-1; i >= 0; i--) 
			{
            	switch (map[j,i].type) 
            	{
            		case MapNoteType.room:
            		  	color = Color.red;
            		  break;
            		case MapNoteType.door:
            		  	color = Color.green;
            		  break;
            		case MapNoteType.way:
            		  	color = Color.yellow;
            		  break;
            		case MapNoteType.hide_way:
            			color = Color.cyan;
            		  break;
            		case MapNoteType.stair_up:
            			color = Color.black;
            		  break;
            		case MapNoteType.stair_down:
            		  	color = Color.white;
            		  break;
            		default:
            		  break;
            	}
            	if (map[j,i].type != MapNoteType.nothing) 
            	{
            		pos_1 = startPos + new Vector3((j+0.5f)*noteSize - 0.5f*noteSize, 1f, (i+0.5f)*noteSize + 0.5f*noteSize);
	                pos_2 = startPos + new Vector3((j+0.5f)*noteSize + 0.5f*noteSize, 1f, (i+0.5f)*noteSize + 0.5f*noteSize);
	                pos_3 = startPos + new Vector3((j+0.5f)*noteSize + 0.5f*noteSize, 1f, (i+0.5f)*noteSize - 0.5f*noteSize);
	                pos_4 = startPos + new Vector3((j+0.5f)*noteSize - 0.5f*noteSize, 1f, (i+0.5f)*noteSize - 0.5f*noteSize);

            		UnityEngine.Debug.DrawLine(pos_1, pos_2, color);
	                UnityEngine.Debug.DrawLine(pos_2, pos_3, color);
	                UnityEngine.Debug.DrawLine(pos_3, pos_4, color);
	                UnityEngine.Debug.DrawLine(pos_4, pos_1, color);

	                UnityEngine.Debug.DrawLine(pos_1, pos_3, color);
	                UnityEngine.Debug.DrawLine(pos_2, pos_4, color);
            	}
                
                //画出 linker
                if (map[j,i].type == MapNoteType.door || map[j,i].type == MapNoteType.stair_up) 
                {
                	color = Color.yellow;
                	pos_1 = startPos + new Vector3((j+0.5f)*noteSize, 2f, (i+0.5f)*noteSize);
                	if (map[j,i].holderDoor.linker != null) 
                	{
                		pos_2 = startPos + new Vector3((map[j,i].holderDoor.linker.x+0.5f)*noteSize, 1f, (map[j,i].holderDoor.linker.y+0.5f)*noteSize);
                	}
                	else
                	{
                		pos_2 = startPos + new Vector3((j+0.5f)*noteSize, 2f, (i+0.5f)*noteSize);
                	}
                	if (map[j,i].holderDoor.type == DoorType.hide) 
                	{
                		if (map[j,i].holderDoor.linker == null) 
                		{
                			//Debug.Log("囧");
                		}
                		else if (map[j,i].holderDoor.linker.holderDoor != null && map[j,i].holderDoor.linker.holderDoor.type == DoorType.hide) 
                		{
                			color = Color.blue;
                		}
                		else
                		{
                			color = Color.cyan;
                		}
                	}
	                UnityEngine.Debug.DrawLine(pos_1, pos_2, color);
                }
            }
        }
    }
}
